package com.gitee.huanminabc.db_freedom.wrapper;

import com.baomidou.mybatisplus.annotation.TableField;
import com.gitee.huanminabc.db_freedom.utils.SqlFunction;
import com.gitee.huanminabc.db_freedom.config.MybatisFreedomConfig;
import com.gitee.huanminabc.db_freedom.proxy.ProxyCglibFactory;
import com.gitee.huanminabc.db_freedom.proxy.ProxyCglibInstance;
import com.gitee.huanminabc.db_freedom.segments.SqlStructureImplSegments;
import com.gitee.huanminabc.utils_common.base.LambdaUtil;
import com.gitee.huanminabc.utils_common.obj.reflect.MethodUtil;
import com.gitee.huanminabc.utils_common.string.StringUtil;
import lombok.EqualsAndHashCode;

import java.lang.reflect.Method;
import java.util.Map;

@EqualsAndHashCode(callSuper = true)
public class LambdaSQL<T> extends SqlStructureImplSegments<T,LambdaSQL<T>> {



    public LambdaSQL() {
    }


    private LambdaSQL(Class<?> entityClass, SQL sql) {
        super(entityClass, sql);
    }

    //创建
    public static <T> LambdaSQL<T> create(Class<T> entity){
        LambdaSQL<T> tLambdaSql = new LambdaSQL<>(entity, new SQL());
        ProxyCglibFactory proxyCglibFactory = new ProxyCglibFactory(MybatisFreedomConfig.actionProxy, tLambdaSql);
        Object object = ProxyCglibInstance.getObject(tLambdaSql, proxyCglibFactory);
        return  (LambdaSQL<T>) object;
    }


    //===========================下面是增强方法===========================

    //内部使用
    private static <T> LambdaSQL<T> create_(Class<?> entity){
        return  create((Class<T>) entity);
    }

    //子查询
    public <Y> LambdaSQL<T> SON(LambdaSQL<Y> LambdaSQL){
        SQL().append(LEFT_BRACKET()).append(LambdaSQL.SQL().getSql()).append(RIGHT_BRACKET());
        SQL().addParamAll(LambdaSQL.SQL().getParams());
        return typedThis;
    }
    public <Y> LambdaSQL<T> SON(Class<Y> entityClass, SqlFunction<LambdaSQL<Y>,LambdaSQL<Y>> sqlFunctions) {
        LambdaSQL<Y> objectLambdaSQL = LambdaSQL.create_(entityClass);
        LambdaSQL<Y> apply = sqlFunctions.apply(objectLambdaSQL);
        SQL().append(LEFT_BRACKET()).append(apply.SQL().getSql()).append(RIGHT_BRACKET());
        SQL().addParamAll(apply.SQL().getParams());
        return typedThis;
    }


    public <Y> LambdaSQL<T> SON(SqlFunction<LambdaSQL<T>,LambdaSQL<T>>  sqlFunctions) {
        Class<?> entityClass = this.getTableInfo().getEntityClass();
        LambdaSQL<T> apply = sqlFunctions.apply(LambdaSQL.create_(entityClass));
        SQL().append("(").append(apply.SQL().getSql()).append(")");
        SQL().addParamAll(apply.SQL().getParams());
        return typedThis;

    }
    //列 全部满足条件的字段
    public <Y> LambdaSQL<T> COLUMN() {
        //获取当前类的所有字段
        Class<?> entityClass = getTableInfo().getEntityClass();
        //只获取可访问的方法
        Method[] declaredMethods = entityClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            //跳过Object的方法
            if(declaredMethod.getDeclaringClass().equals(Object.class)){
                continue;
            }
            String name = declaredMethod.getName();
            //排除注解上是忽略的@TableField(exist = false)
            TableField annotation = declaredMethod.getAnnotation(TableField.class);
            if(annotation!=null&& !annotation.exist()){
                continue;
            }
            //判断方法是get set is
            if(name.startsWith("get")||name.startsWith("is")){
                String property = MethodUtil.methodToProperty(name);
                SQL().append(getTableInfo().getAlias()).append(".").append(property).append(",");
            }
        }
        SQL().deleteCharAt(SQL().length()-1);
        return typedThis;
    }
    public  <Y> LambdaSQL<T> COLUMN(SqlFunction<Y,?>... columns){
        for (SqlFunction<Y,?> column : columns) {
            Map<String, Object> stringObjectMap = LambdaUtil.lambdaInvocation(column);
            String field = stringObjectMap.get("fieldName").toString();
            Class<?> entityClass = (Class<?>) stringObjectMap.get("clazz");
            //判断是不是当前类
            if(entityClass.equals(getTableInfo().getEntityClass())) {
                SQL().append(getTableInfo().getAlias()).append(".").append(field).append(",");

            }else{
                TableInfo tableInfo = getEntityClassList().get(entityClass.getName());
                //如果是空那么就表示没有加入
                if(tableInfo==null){
                    throw new RuntimeException(entityClass.getName()+"没有加入表,无法使用COLUMN方法");
                }
                SQL().append(tableInfo.getAlias()).append(".").append(field).append(",");
            }
        }
        SQL().deleteCharAt(SQL().length()-1);
        return typedThis;
    }


    //加field  ,兼容多表查询 on
    public  <Y> LambdaSQL<T> FIELD(SqlFunction<Y,?> column){
        Map<String, Object> stringObjectMap = LambdaUtil.lambdaInvocation(column);
        String field = stringObjectMap.get("fieldName").toString();
        Class<?> entityClass = (Class<?>) stringObjectMap.get("clazz");
        //判断是不是当前类
        if(entityClass.equals(getTableInfo().getEntityClass())) {
            SQL().append(getTableInfo().getAlias()).append(".").append(field).append(" ");
        }else{
            TableInfo tableInfo = getEntityClassList().get(entityClass.getName());
            //如果是空那么就表示没有加入
            if(tableInfo==null){
                throw new RuntimeException(entityClass.getName()+"没有加入表,无法使用FIELD方法");
            }
            SQL().append(tableInfo.getAlias()).append(".").append(field).append(" ");
        }
        return typedThis;
    }



    //加表 ,用于 联合查询 xxx join  表名
    public LambdaSQL<T> TABLE(Class<?> entityClass){
        //检测上一个操作必须是 LEFT_JOIN RIGHT_JOIN INNER_JOIN
        String operation = this.getOperationRecord().get(this.getOperationRecord().size() - 1);
        //如果是联合查询那么必须在联合查询之后
        if(StringUtil.equalsAny(operation,"LEFT_JOIN","RIGHT_JOIN","INNER_JOIN")){
            throw new RuntimeException("TABLE方法必须在LEFT_JOIN RIGHT_JOIN INNER_JOIN方法之后");
        }
        //加入表信息
        TableInfo tableInfo = this.createTableInfo(entityClass);
        //sql拼接
        SQL().append(" ").append(tableInfo.getTableName()).append(" ").append(tableInfo.getAlias()).append(" ");
        return typedThis;
    }

    //加param
    public LambdaSQL<T> V(Object param){
        SQL().append(" ? ");
        SQL().addParam(param);
        return typedThis;
    }

    //eq的同时加参
    public LambdaSQL<T> EQ( Object param){
        SQL().append(EQ()).append(V(param));
        return typedThis;
    }


    public LambdaSQL<T> GT(Object param) {
        SQL().append(GT()).append(V(param));
        return typedThis;
    }
    public LambdaSQL<T> GE(Object param) {
        SQL().append(GE()).append(V(param));
        return typedThis;
    }
    public LambdaSQL<T> LT(Object param) {
        SQL().append(LT()).append(V(param));
        return typedThis;
    }
    public LambdaSQL<T> LE(Object param) {
        SQL().append(LE()).append(V(param));
        return typedThis;
    }
    public LambdaSQL<T> NE(Object param) {
        SQL().append(NE()).append(V(param));
        return typedThis;
    }

    public LambdaSQL<T> LIKE(Object param) {
        //必须是String类型
        if(!(param instanceof String)){
            throw new RuntimeException("LIKE方法只能接受String类型");
        }
        SQL().append(LIKE()).append(V("%".concat(param.toString()).concat("%")));
        return typedThis;
    }

    public LambdaSQL<T> LIKE_LEFT(Object param) {
        //必须是String类型
        if(!(param instanceof String)){
            throw new RuntimeException("LIKE_LEFT方法只能接受String类型");
        }
        SQL().append(LIKE()).append(V("%".concat(param.toString())));
        return typedThis;
    }

    public LambdaSQL<T> LIKE_RIGHT(Object param) {
        //必须是String类型
        if(!(param instanceof String)){
            throw new RuntimeException("LIKE_RIGHT方法只能接受String类型");
        }
        SQL().append(" LIKE ? ");
        SQL().addParam(param.toString().concat("%"));
        return typedThis;
    }

    public LambdaSQL<T> IN(Object... params) {
        SQL().append(" IN ").append(LEFT_BRACKET());
        for (Object param : params) {
            SQL().append("?,");
            SQL().addParam(param);
        }
        SQL().deleteCharAt(SQL().length()-1);
        SQL().append(RIGHT_BRACKET());
        return typedThis;
    }

    //LIMIT
    public LambdaSQL<T> LIMIT(Object param) {
        SQL().append(LIMIT()).append(V(param));
        return typedThis;
    }
    //LIMIT OFFSET
    public LambdaSQL<T> LIMIT(Object limit,Object offset) {
        SQL().append(LIMIT()).append(V(limit)).append(OFFSET()).append(V(offset));
        return typedThis;
    }
    //OFFSET
    public LambdaSQL<T> OFFSET(Object param) {
        SQL().append(OFFSET()).append(V(param));
        return typedThis;
    }


}
